import asyncio
import json
import uvicorn
import websockets
from contextlib import asynccontextmanager
from fastapi import FastAPI
from websockets.exceptions import ConnectionClosed
from msg_handle import handle_message
from msg_sender import worker_process
from config import WEBSOCKET_URI
from logger import logger_main

msg_set = set()


class WeChatBotClient:
    def __init__(self):
        self.websocket = None
        self.is_connected = False
        self.reconnect_interval = 5

    async def connect(self):
        """连接到 WeChat Bot WebSocket 服务"""
        logger_main.info(f"正在连接 WebSocket")
        while True:
            try:
                async with websockets.connect(WEBSOCKET_URI) as websocket:
                    self.websocket = websocket
                    self.is_connected = True
                    logger_main.info("WebSocket 连接成功")
                    # 监听消息
                    await self.listen_messages()

            except ConnectionClosed:
                logger_main.warning("WebSocket 连接已关闭")
                self.is_connected = False
            except Exception as e:
                logger_main.error(f"❌ WebSocket 连接错误: {e}")
                self.is_connected = False

            # 重连逻辑
            logger_main.info(f"{self.reconnect_interval}秒后尝试重新连接...")
            await asyncio.sleep(self.reconnect_interval)

    async def listen_messages(self):
        """监听并处理接收到的消息"""
        if self.websocket is None:
            logger_main.error("WebSocket 连接未建立")
            return
        try:
            async for message in self.websocket:
                try:
                    json_data = json.loads(message)
                    tip_message = json_data.get(
                        "Data", {}).get("syncData", {}).get("Message", "")
                    if tip_message == "当前未有新消息":
                        continue
                    Success = json_data.get(
                        "Data", {}).get("syncData", {}).get("Success", False)
                    if not Success:
                        if "i/o timeout" in tip_message:
                            # logger_main.warning("接收信息，连接超时")
                            pass
                        else:
                            logger_main.debug(f"接收到消息: {message}")
                            logger_main.warning("未知错误信息，请查看日志，进行处理")
                        continue
                    logger_main.debug(f"接收到消息: {message}")
                    AddMsgs = json_data.get(
                        "Data", {}).get("syncData", {}).get("Data", {}).get("AddMsgs", [])
                    logger_main.info(f"消息数组长度，共 {len(AddMsgs)} 条")
                    if len(AddMsgs) >= 20:
                        logger_main.warning("单次接收消息过多，已忽略，防止消息风暴")
                        continue
                    for msg in AddMsgs:
                        NewMsgId = msg.get("NewMsgId", None)
                        if NewMsgId in msg_set:
                            logger_main.info(f"消息 {NewMsgId} 已处理，跳过。")
                            continue
                        msg_set.add(NewMsgId)
                        try:
                            await handle_message(msg)
                        except Exception as e:
                            logger_main.error(f"❌ 处理信息时出现错误,错误原因: {e}")
                            continue

                except Exception as e:
                    logger_main.error(f"❌ 接收信息时出现错误,错误原因: {e}")
                    continue
        except Exception as e:
            logger_main.error(f"❌ 监听消息时出错: {e}")
            raise


# 全局 WebSocket 客户端实例
wechat_client = WeChatBotClient()


@asynccontextmanager
async def lifespan(app: FastAPI):
    """应用生命周期管理"""
    # 启动时
    logger_main.info("正在启动应用...")
    # 创建后台任务连接 WebSocket
    task = asyncio.create_task(wechat_client.connect())
    worker = asyncio.create_task(worker_process())
    yield
    # 关闭时
    logger_main.info("正在关闭应用...")
    task.cancel()
    worker.cancel()
    try:
        await task
    except asyncio.CancelledError:
        pass

app = FastAPI(lifespan=lifespan)

if __name__ == "__main__":
    uvicorn.run(
        "main:app",
        host="0.0.0.0",
        port=23145
    )
